package com.zycfc.zsf.boot.redis.autoconfigure;

import org.springframework.boot.context.properties.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.data.redis.*;
import com.zycfc.zsf.boot.serializer.autoconfigure.*;
import org.springframework.context.annotation.*;
import org.springframework.beans.factory.annotation.*;
import com.zycfc.zsf.boot.serializer.*;
import com.zycfc.zsf.boot.redis.*;
import org.springframework.boot.autoconfigure.condition.*;
import com.zycfc.zsf.boot.util.text.*;
import redis.clients.jedis.*;
import org.apache.commons.pool2.impl.*;
import java.time.*;
import org.springframework.data.redis.connection.jedis.*;
import com.zycfc.zsf.boot.util.collection.*;
import org.springframework.data.redis.connection.*;
import java.util.*;
import com.google.common.util.concurrent.*;
import java.util.concurrent.*;
import com.zycfc.zsf.boot.redis.util.*;
import org.slf4j.*;

@Configuration
@EnableConfigurationProperties({ RedisProperties.class })
@AutoConfigureAfter({ RedisAutoConfiguration.class, SerializerAutoConfiguration.class })
@ConditionalOnProperty(value = { "zsf.boot.redis.enabled" }, matchIfMissing = true)
public class RedisOperaterAutoConfigure
{
    private static final Logger log;
    @Value("${zsf.all.namespace:dev}")
    private String namespace;
    @Value("${spring.application.name}")
    private String appName;
    @Value("${spring.profiles.active}")
    private String profilesActive;
    private static final String REDIS_FILE_SCHEDULE_THREAD_NAME_FORMAT = "redisFileSchedule-pool-%d";
    private static final String REDIS_CALL_STAT_THREAD_NAME_FORMAT = "redisFileSchedule-pool-%d";
    private static final String CONN_FACTORY_TYPE_MAIN = "main";
    private static final String CONN_FACTORY_TYPE_EXT = "ext";
    private static final String COMMA_SEPARATOR = ",";
    
    @Bean(name = { "redisConnectionFactory" })
    @Primary
    public RedisConnectionFactory constructPrimaryConnectionFactory(final RedisProperties config) {
        return this.constructConnectionFactory(config, "main");
    }
    
    @Bean(name = { "extRedisConnectionFactory" })
    @ConditionalOnExpression("(environment.getProperty('zsf.boot.redis.extNodes')!=null) && (!''.equals(environment.getProperty('zsf.boot.redis.extNodes')))")
    public RedisConnectionFactory constructExtConnectionFactory(final RedisProperties config) {
        return this.constructConnectionFactory(config, "ext");
    }
    
    @Bean
    @Primary
    @ConditionalOnMissingBean({ IRedisOperater.class })
    public IRedisOperater redisOperater(@Qualifier("redisConnectionFactory") final RedisConnectionFactory connectionFactory, final RedisProperties config, final ISerializer zsfSerializer) {
        final IRedisOperater redisOperater = RedisOperaterFactory.create(config, zsfSerializer, connectionFactory, this.appName, this.profilesActive);
        RedisOperaterAutoConfigure.log.info("^_^Redis\u7f13\u5b58\u7ec4\u4ef6\u52a0\u8f7d\u6210\u529f^_^");
        return redisOperater;
    }
    
    @Bean(name = { "extRedisOperater" })
    @ConditionalOnExpression("(environment.getProperty('zsf.boot.redis.extNodes')!=null) && (!''.equals(environment.getProperty('zsf.boot.redis.extNodes')))")
    public IRedisOperater extRedisOperater(@Qualifier("extRedisConnectionFactory") final RedisConnectionFactory extConnectionFactory, final RedisProperties config, final ISerializer zsfSerializer) {
        if (StringUtils.isEmpty(config.getExtNodes())) {
            RedisOperaterAutoConfigure.log.error("Redis\u6269\u5c55\u96c6\u7fa4extNodes\u914d\u7f6e\u4e3a\u7a7a");
            return null;
        }
        final IRedisOperater extRedisOperater = RedisOperaterFactory.create(config, zsfSerializer, extConnectionFactory, this.appName, this.profilesActive);
        RedisOperaterAutoConfigure.log.info("^_^Redis\u6269\u5c55\u96c6\u7fa4\u7ec4\u4ef6\u52a0\u8f7d\u6210\u529f^_^");
        return extRedisOperater;
    }
    
    private RedisConnectionFactory constructConnectionFactory(final RedisProperties config, final String type) {
        final boolean mainFlag = "main".equals(type);
        final String nodes = mainFlag ? config.getNodes() : config.getExtNodes();
        RedisOperaterAutoConfigure.log.info(type + "-redis-nodes:{}", (Object)nodes);
        final String[] nodesArray = nodes.split(",");
        RedisConnectionFactory redisConnectionFactory = null;
        String password = mainFlag ? config.getPassword() : config.getExtPassword();
        if (mainFlag) {
            final List<String> envBlackList = new ArrayList<String>();
            envBlackList.add("prod");
            if (StringUtils.isEmpty(password) && !envBlackList.contains(this.namespace)) {
                password = "Zycfc@2020";
            }
            if ("666".equals(password)) {
                password = null;
            }
        }
        final RedisPassword redisPassWord = StringUtils.isEmpty(password) ? RedisPassword.none() : RedisPassword.of(password);
        if (nodesArray.length == 1) {
            final RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
            final String[] server = nodesArray[0].split(":");
            redisStandaloneConfiguration.setHostName(server[0]);
            redisStandaloneConfiguration.setPort(Integer.parseInt(server[1]));
            redisStandaloneConfiguration.setPassword(redisPassWord);
            final JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            jedisPoolConfig.setMaxTotal(500);
            jedisPoolConfig.setMaxIdle(50);
            jedisPoolConfig.setMinIdle(20);
            jedisPoolConfig.setMaxWaitMillis(1000L);
            jedisPoolConfig.setBlockWhenExhausted(true);
            jedisPoolConfig.setTestOnBorrow(false);
            jedisPoolConfig.setTestOnReturn(false);
            jedisPoolConfig.setJmxEnabled(true);
            jedisPoolConfig.setTestWhileIdle(true);
            jedisPoolConfig.setTimeBetweenEvictionRunsMillis(30000L);
            jedisPoolConfig.setMinEvictableIdleTimeMillis(300000L);
            jedisPoolConfig.setNumTestsPerEvictionRun(-1);
            final JedisClientConfiguration.DefaultJedisClientConfigurationBuilder builder = (JedisClientConfiguration.DefaultJedisClientConfigurationBuilder)JedisClientConfiguration.builder();
            builder.usePooling();
            builder.poolConfig((GenericObjectPoolConfig)jedisPoolConfig);
            builder.connectTimeout(Duration.ofMillis(3000L));
            builder.readTimeout(Duration.ofMillis(2000L));
            final JedisClientConfiguration clientConfig = builder.build();
            redisConnectionFactory = (RedisConnectionFactory)new JedisConnectionFactory(redisStandaloneConfiguration, clientConfig);
        }
        else if (nodesArray.length > 1) {
            final RedisClusterConfiguration configuration = new RedisClusterConfiguration((Collection)ArrayUtils.asList((Object[])nodesArray));
            configuration.setMaxRedirects(10);
            configuration.setPassword(redisPassWord);
            final JedisPoolConfig jedisPoolConfig2 = new JedisPoolConfig();
            jedisPoolConfig2.setMaxTotal(200);
            jedisPoolConfig2.setMaxIdle(50);
            jedisPoolConfig2.setMinIdle(5);
            jedisPoolConfig2.setMaxWaitMillis(1000L);
            jedisPoolConfig2.setBlockWhenExhausted(true);
            jedisPoolConfig2.setTestOnBorrow(false);
            jedisPoolConfig2.setTestOnReturn(false);
            jedisPoolConfig2.setJmxEnabled(true);
            jedisPoolConfig2.setTestWhileIdle(true);
            jedisPoolConfig2.setTimeBetweenEvictionRunsMillis(30000L);
            jedisPoolConfig2.setMinEvictableIdleTimeMillis(300000L);
            jedisPoolConfig2.setNumTestsPerEvictionRun(-1);
            final JedisClientConfiguration.JedisClientConfigurationBuilder configurationBuilder = JedisClientConfiguration.builder();
            final JedisClientConfiguration jedisClientConfiguration = configurationBuilder.usePooling().poolConfig((GenericObjectPoolConfig)jedisPoolConfig2).build();
            redisConnectionFactory = (RedisConnectionFactory)new JedisConnectionFactory(configuration, jedisClientConfiguration);
        }
        return redisConnectionFactory;
    }
    
    @Bean
    public ScheduledExecutorService redisFileSchedule() {
        final ThreadFactory redisFileScheduleThreadFactory = new ThreadFactoryBuilder().setNameFormat("redisFileSchedule-pool-%d").build();
        final ScheduledExecutorService redisFileScheduleService;
        final ScheduledThreadPoolExecutor threadPoolExecutor = (ScheduledThreadPoolExecutor)(redisFileScheduleService = new ScheduledThreadPoolExecutor(1, redisFileScheduleThreadFactory, new ThreadPoolExecutor.CallerRunsPolicy()));
        redisFileScheduleService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                RedisLocalFileUtil.writeToFile();
            }
        }, 30L, 10L, TimeUnit.SECONDS);
        return redisFileScheduleService;
    }
    
    @Bean
    public ScheduledExecutorService redisCallStatSchedule() {
        final ThreadFactory redisCallStatThreadFactory = new ThreadFactoryBuilder().setNameFormat("redisFileSchedule-pool-%d").build();
        final ScheduledExecutorService redisCallStatScheduleService;
        final ScheduledThreadPoolExecutor threadPoolExecutor = (ScheduledThreadPoolExecutor)(redisCallStatScheduleService = new ScheduledThreadPoolExecutor(1, redisCallStatThreadFactory, new ThreadPoolExecutor.CallerRunsPolicy()));
        redisCallStatScheduleService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                RedisCallStatUtil.doRedisCallStat();
            }
        }, 30L, 60L, TimeUnit.SECONDS);
        return redisCallStatScheduleService;
    }
    
    static {
        log = LoggerFactory.getLogger((Class)RedisOperaterAutoConfigure.class);
    }
}
